//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
// Copyright(C) 2014-2015 Alexey Khokholov
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//   System interface for music.
//

#pragma once

#include "../interface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MIDI_CHANNELS_PER_TRACK 16

#define MIDI_EVENT_NOTE_OFF 0x80
#define MIDI_EVENT_NOTE_ON 0x90

#define MIDI_CONTROLLER_MAIN_VOLUME 0x7
#define MIDI_CONTROLLER_PAN 0xa
#define MIDI_CONTROLLER_ALL_NOTES_OFF 0x7b
#define MIDI_EVENT_CONTROLLER 0xb0
#define MIDI_EVENT_PROGRAM_CHANGE 0xc0
#define MIDI_EVENT_CHAN_AFTERTOUCH 0xd0
#define MIDI_EVENT_PITCH_BEND 0xe0

#define OPL_NUM_OPERATORS 21
#define OPL_NUM_VOICES 9

#define OPL_REG_WAVEFORM_ENABLE 0x01
#define OPL_REG_TIMER1 0x02
#define OPL_REG_TIMER2 0x03
#define OPL_REG_TIMER_CTRL 0x04
#define OPL_REG_FM_MODE 0x08

#define OPL_REGS_TREMOLO 0x20
#define OPL_REGS_LEVEL 0x40
#define OPL_REGS_ATTACK 0x60
#define OPL_REGS_SUSTAIN 0x80
#define OPL_REGS_WAVEFORM 0xE0

#define OPL_REGS_FREQ_1 0xA0
#define OPL_REGS_FREQ_2 0xB0
#define OPL_REGS_FEEDBACK 0xC0

#define OPL_REG_NEW_MODE 0x105

#define GENMIDI_NUM_INSTRS 128
#define GENMIDI_NUM_PERCUSSION 47

#define GENMIDI_HEADER "#OPL_II#"
#define GENMIDI_FLAG_FIXED 0x0001 /* fixed pitch */
#define GENMIDI_FLAG_2VOICE 0x0004 /* double voice (OPL3) */

#define PERCUSSION_LOG_LEN 16

typedef unsigned char byte;

#pragma pack(1)
typedef struct
{
	byte tremolo;
	byte attack;
	byte sustain;
	byte waveform;
	byte scale;
	byte level;
} genmidi_op_t;
#pragma pack()

#pragma pack(1)
typedef struct
{
	genmidi_op_t modulator;
	byte feedback;
	genmidi_op_t carrier;
	byte unused;
	short base_note_offset;
} genmidi_voice_t;
#pragma pack()

#pragma pack(1)
typedef struct
{
	unsigned short flags;
	byte fine_tuning;
	byte fixed_note;

	genmidi_voice_t voices[2];
} genmidi_instr_t;
#pragma pack()

// Data associated with a channel of a track that is currently playing.

typedef struct
{
	// The instrument currently used for this track.

	const genmidi_instr_t *instrument;

	// Volume level

	int volume;

	// Pan value

	int pan, panex;

	// Pitch bend value:

	int bend;

} opl_channel_data_t;

typedef struct opl_voice_s opl_voice_t;

struct opl_voice_s {
	// Index of this voice:
	int index;

	// The operators used by this voice:
	int op1, op2;

	// Array of this voice
	int array;

	// Currently-loaded instrument data
	const genmidi_instr_t *current_instr;

	// The voice number in the instrument to use.
	// This is normally set to zero; if this is a double voice
	// instrument, it may be one.
	unsigned int current_instr_voice;

	// The channel currently using this voice.
	opl_channel_data_t *channel;

	// The midi key that this voice is playing.
	unsigned int key;

	// The note being played.  This is normally the same as
	// the key, but if the instrument is a fixed pitch
	// instrument, it is different.
	unsigned int note;

	// The frequency value being used.
	unsigned int freq;

	// The volume of the note being played on this channel.
	unsigned int note_volume;

	// The current volume (register value) that has been set for this channel.
	unsigned int car_volume;
	unsigned int mod_volume;

	// The current pan.
	unsigned int reg_pan;

	// Priority.
	unsigned int priority;
};

typedef enum {
	opl_doom1_1_666, // Doom 1 v1.666
	opl_doom2_1_666, // Doom 2 v1.666, Hexen, Heretic
	opl_doom_1_9 // Doom v1.9, Strife
} opl_driver_ver_t;

// Operators used by the different voices.

const int voice_operators[2][OPL_NUM_VOICES] = {
	{ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 },
	{ 0x03, 0x04, 0x05, 0x0b, 0x0c, 0x0d, 0x13, 0x14, 0x15 }
};

// Frequency values to use for each note.

const unsigned short frequency_curve[] = {

	0x133,
	0x133,
	0x134,
	0x134,
	0x135,
	0x136,
	0x136,
	0x137, // -1
	0x137,
	0x138,
	0x138,
	0x139,
	0x139,
	0x13a,
	0x13b,
	0x13b,
	0x13c,
	0x13c,
	0x13d,
	0x13d,
	0x13e,
	0x13f,
	0x13f,
	0x140,
	0x140,
	0x141,
	0x142,
	0x142,
	0x143,
	0x143,
	0x144,
	0x144,

	0x145,
	0x146,
	0x146,
	0x147,
	0x147,
	0x148,
	0x149,
	0x149, // -2
	0x14a,
	0x14a,
	0x14b,
	0x14c,
	0x14c,
	0x14d,
	0x14d,
	0x14e,
	0x14f,
	0x14f,
	0x150,
	0x150,
	0x151,
	0x152,
	0x152,
	0x153,
	0x153,
	0x154,
	0x155,
	0x155,
	0x156,
	0x157,
	0x157,
	0x158,

	// These are used for the first seven MIDI note values:

	0x158,
	0x159,
	0x15a,
	0x15a,
	0x15b,
	0x15b,
	0x15c,
	0x15d, // 0
	0x15d,
	0x15e,
	0x15f,
	0x15f,
	0x160,
	0x161,
	0x161,
	0x162,
	0x162,
	0x163,
	0x164,
	0x164,
	0x165,
	0x166,
	0x166,
	0x167,
	0x168,
	0x168,
	0x169,
	0x16a,
	0x16a,
	0x16b,
	0x16c,
	0x16c,

	0x16d,
	0x16e,
	0x16e,
	0x16f,
	0x170,
	0x170,
	0x171,
	0x172, // 1
	0x172,
	0x173,
	0x174,
	0x174,
	0x175,
	0x176,
	0x176,
	0x177,
	0x178,
	0x178,
	0x179,
	0x17a,
	0x17a,
	0x17b,
	0x17c,
	0x17c,
	0x17d,
	0x17e,
	0x17e,
	0x17f,
	0x180,
	0x181,
	0x181,
	0x182,

	0x183,
	0x183,
	0x184,
	0x185,
	0x185,
	0x186,
	0x187,
	0x188, // 2
	0x188,
	0x189,
	0x18a,
	0x18a,
	0x18b,
	0x18c,
	0x18d,
	0x18d,
	0x18e,
	0x18f,
	0x18f,
	0x190,
	0x191,
	0x192,
	0x192,
	0x193,
	0x194,
	0x194,
	0x195,
	0x196,
	0x197,
	0x197,
	0x198,
	0x199,

	0x19a,
	0x19a,
	0x19b,
	0x19c,
	0x19d,
	0x19d,
	0x19e,
	0x19f, // 3
	0x1a0,
	0x1a0,
	0x1a1,
	0x1a2,
	0x1a3,
	0x1a3,
	0x1a4,
	0x1a5,
	0x1a6,
	0x1a6,
	0x1a7,
	0x1a8,
	0x1a9,
	0x1a9,
	0x1aa,
	0x1ab,
	0x1ac,
	0x1ad,
	0x1ad,
	0x1ae,
	0x1af,
	0x1b0,
	0x1b0,
	0x1b1,

	0x1b2,
	0x1b3,
	0x1b4,
	0x1b4,
	0x1b5,
	0x1b6,
	0x1b7,
	0x1b8, // 4
	0x1b8,
	0x1b9,
	0x1ba,
	0x1bb,
	0x1bc,
	0x1bc,
	0x1bd,
	0x1be,
	0x1bf,
	0x1c0,
	0x1c0,
	0x1c1,
	0x1c2,
	0x1c3,
	0x1c4,
	0x1c4,
	0x1c5,
	0x1c6,
	0x1c7,
	0x1c8,
	0x1c9,
	0x1c9,
	0x1ca,
	0x1cb,

	0x1cc,
	0x1cd,
	0x1ce,
	0x1ce,
	0x1cf,
	0x1d0,
	0x1d1,
	0x1d2, // 5
	0x1d3,
	0x1d3,
	0x1d4,
	0x1d5,
	0x1d6,
	0x1d7,
	0x1d8,
	0x1d8,
	0x1d9,
	0x1da,
	0x1db,
	0x1dc,
	0x1dd,
	0x1de,
	0x1de,
	0x1df,
	0x1e0,
	0x1e1,
	0x1e2,
	0x1e3,
	0x1e4,
	0x1e5,
	0x1e5,
	0x1e6,

	0x1e7,
	0x1e8,
	0x1e9,
	0x1ea,
	0x1eb,
	0x1ec,
	0x1ed,
	0x1ed, // 6
	0x1ee,
	0x1ef,
	0x1f0,
	0x1f1,
	0x1f2,
	0x1f3,
	0x1f4,
	0x1f5,
	0x1f6,
	0x1f6,
	0x1f7,
	0x1f8,
	0x1f9,
	0x1fa,
	0x1fb,
	0x1fc,
	0x1fd,
	0x1fe,
	0x1ff,
	0x200,
	0x201,
	0x201,
	0x202,
	0x203,

	// First note of looped range used for all octaves:

	0x204,
	0x205,
	0x206,
	0x207,
	0x208,
	0x209,
	0x20a,
	0x20b, // 7
	0x20c,
	0x20d,
	0x20e,
	0x20f,
	0x210,
	0x210,
	0x211,
	0x212,
	0x213,
	0x214,
	0x215,
	0x216,
	0x217,
	0x218,
	0x219,
	0x21a,
	0x21b,
	0x21c,
	0x21d,
	0x21e,
	0x21f,
	0x220,
	0x221,
	0x222,

	0x223,
	0x224,
	0x225,
	0x226,
	0x227,
	0x228,
	0x229,
	0x22a, // 8
	0x22b,
	0x22c,
	0x22d,
	0x22e,
	0x22f,
	0x230,
	0x231,
	0x232,
	0x233,
	0x234,
	0x235,
	0x236,
	0x237,
	0x238,
	0x239,
	0x23a,
	0x23b,
	0x23c,
	0x23d,
	0x23e,
	0x23f,
	0x240,
	0x241,
	0x242,

	0x244,
	0x245,
	0x246,
	0x247,
	0x248,
	0x249,
	0x24a,
	0x24b, // 9
	0x24c,
	0x24d,
	0x24e,
	0x24f,
	0x250,
	0x251,
	0x252,
	0x253,
	0x254,
	0x256,
	0x257,
	0x258,
	0x259,
	0x25a,
	0x25b,
	0x25c,
	0x25d,
	0x25e,
	0x25f,
	0x260,
	0x262,
	0x263,
	0x264,
	0x265,

	0x266,
	0x267,
	0x268,
	0x269,
	0x26a,
	0x26c,
	0x26d,
	0x26e, // 10
	0x26f,
	0x270,
	0x271,
	0x272,
	0x273,
	0x275,
	0x276,
	0x277,
	0x278,
	0x279,
	0x27a,
	0x27b,
	0x27d,
	0x27e,
	0x27f,
	0x280,
	0x281,
	0x282,
	0x284,
	0x285,
	0x286,
	0x287,
	0x288,
	0x289,

	0x28b,
	0x28c,
	0x28d,
	0x28e,
	0x28f,
	0x290,
	0x292,
	0x293, // 11
	0x294,
	0x295,
	0x296,
	0x298,
	0x299,
	0x29a,
	0x29b,
	0x29c,
	0x29e,
	0x29f,
	0x2a0,
	0x2a1,
	0x2a2,
	0x2a4,
	0x2a5,
	0x2a6,
	0x2a7,
	0x2a9,
	0x2aa,
	0x2ab,
	0x2ac,
	0x2ae,
	0x2af,
	0x2b0,

	0x2b1,
	0x2b2,
	0x2b4,
	0x2b5,
	0x2b6,
	0x2b7,
	0x2b9,
	0x2ba, // 12
	0x2bb,
	0x2bd,
	0x2be,
	0x2bf,
	0x2c0,
	0x2c2,
	0x2c3,
	0x2c4,
	0x2c5,
	0x2c7,
	0x2c8,
	0x2c9,
	0x2cb,
	0x2cc,
	0x2cd,
	0x2ce,
	0x2d0,
	0x2d1,
	0x2d2,
	0x2d4,
	0x2d5,
	0x2d6,
	0x2d8,
	0x2d9,

	0x2da,
	0x2dc,
	0x2dd,
	0x2de,
	0x2e0,
	0x2e1,
	0x2e2,
	0x2e4, // 13
	0x2e5,
	0x2e6,
	0x2e8,
	0x2e9,
	0x2ea,
	0x2ec,
	0x2ed,
	0x2ee,
	0x2f0,
	0x2f1,
	0x2f2,
	0x2f4,
	0x2f5,
	0x2f6,
	0x2f8,
	0x2f9,
	0x2fb,
	0x2fc,
	0x2fd,
	0x2ff,
	0x300,
	0x302,
	0x303,
	0x304,

	0x306,
	0x307,
	0x309,
	0x30a,
	0x30b,
	0x30d,
	0x30e,
	0x310, // 14
	0x311,
	0x312,
	0x314,
	0x315,
	0x317,
	0x318,
	0x31a,
	0x31b,
	0x31c,
	0x31e,
	0x31f,
	0x321,
	0x322,
	0x324,
	0x325,
	0x327,
	0x328,
	0x329,
	0x32b,
	0x32c,
	0x32e,
	0x32f,
	0x331,
	0x332,

	0x334,
	0x335,
	0x337,
	0x338,
	0x33a,
	0x33b,
	0x33d,
	0x33e, // 15
	0x340,
	0x341,
	0x343,
	0x344,
	0x346,
	0x347,
	0x349,
	0x34a,
	0x34c,
	0x34d,
	0x34f,
	0x350,
	0x352,
	0x353,
	0x355,
	0x357,
	0x358,
	0x35a,
	0x35b,
	0x35d,
	0x35e,
	0x360,
	0x361,
	0x363,

	0x365,
	0x366,
	0x368,
	0x369,
	0x36b,
	0x36c,
	0x36e,
	0x370, // 16
	0x371,
	0x373,
	0x374,
	0x376,
	0x378,
	0x379,
	0x37b,
	0x37c,
	0x37e,
	0x380,
	0x381,
	0x383,
	0x384,
	0x386,
	0x388,
	0x389,
	0x38b,
	0x38d,
	0x38e,
	0x390,
	0x392,
	0x393,
	0x395,
	0x397,

	0x398,
	0x39a,
	0x39c,
	0x39d,
	0x39f,
	0x3a1,
	0x3a2,
	0x3a4, // 17
	0x3a6,
	0x3a7,
	0x3a9,
	0x3ab,
	0x3ac,
	0x3ae,
	0x3b0,
	0x3b1,
	0x3b3,
	0x3b5,
	0x3b7,
	0x3b8,
	0x3ba,
	0x3bc,
	0x3bd,
	0x3bf,
	0x3c1,
	0x3c3,
	0x3c4,
	0x3c6,
	0x3c8,
	0x3ca,
	0x3cb,
	0x3cd,

	// The last note has an incomplete range, and loops round back to
	// the start.  Note that the last value is actually a buffer overrun
	// and does not fit with the other values.

	0x3cf,
	0x3d1,
	0x3d2,
	0x3d4,
	0x3d6,
	0x3d8,
	0x3da,
	0x3db, // 18
	0x3dd,
	0x3df,
	0x3e1,
	0x3e3,
	0x3e4,
	0x3e6,
	0x3e8,
	0x3ea,
	0x3ec,
	0x3ed,
	0x3ef,
	0x3f1,
	0x3f3,
	0x3f5,
	0x3f6,
	0x3f8,
	0x3fa,
	0x3fc,
	0x3fe,
	0x36c,
};

// Mapping from MIDI volume level to OPL level value.

const unsigned int volume_mapping_table[] = {
	0, 1, 3, 5, 6, 8, 10, 11,
	13, 14, 16, 17, 19, 20, 22, 23,
	25, 26, 27, 29, 30, 32, 33, 34,
	36, 37, 39, 41, 43, 45, 47, 49,
	50, 52, 54, 55, 57, 59, 60, 61,
	63, 64, 66, 67, 68, 69, 71, 72,
	73, 74, 75, 76, 77, 79, 80, 81,
	82, 83, 84, 84, 85, 86, 87, 88,
	89, 90, 91, 92, 92, 93, 94, 95,
	96, 96, 97, 98, 99, 99, 100, 101,
	101, 102, 103, 103, 104, 105, 105, 106,
	107, 107, 108, 109, 109, 110, 110, 111,
	112, 112, 113, 113, 114, 114, 115, 115,
	116, 117, 117, 118, 118, 119, 119, 120,
	120, 121, 121, 122, 122, 123, 123, 123,
	124, 124, 125, 125, 126, 126, 127, 127
};

const unsigned int channel_map_table[MIDI_CHANNELS_PER_TRACK] = {
	0, 1, 2, 3, 4, 5, 6, 7, 8, 15, 9, 10, 11, 12, 13, 14
};

class DoomOPL : public midisynth {
	private:
	fm_chip *opl;
	opl_channel_data_t channels[MIDI_CHANNELS_PER_TRACK];
	opl_driver_ver_t opl_drv_ver = opl_doom_1_9;

	// GENMIDI lump instrument data:

	const genmidi_instr_t *main_instrs;
	const genmidi_instr_t *percussion_instrs;

	// Voices:

	opl_voice_t voices[OPL_NUM_VOICES * 2];
	opl_voice_t *voice_free_list[OPL_NUM_VOICES * 2];
	opl_voice_t *voice_alloced_list[OPL_NUM_VOICES * 2];
	unsigned int voice_free_num = 0;
	unsigned int voice_alloced_num = 0;

	bool opl_new;
	bool opl_extp;
	unsigned int opl_voices;

	void OPL_WriteRegister(unsigned int reg, unsigned char data);
	void OPL_InitRegisters(bool opl_new);
	bool LoadInstrumentTable(unsigned int bank);
	void ReleaseVoice(unsigned int id);
	void LoadOperatorData(int slot, const genmidi_op_t *data, bool max_level, unsigned int *volume);
	void SetVoiceInstrument(opl_voice_t *voice, const genmidi_instr_t *instr, unsigned int instr_voice);
	void SetVoiceVolume(opl_voice_t *voice, unsigned int volume);
	void SetVoicePan(opl_voice_t *voice, unsigned int pan);
	void SetVoicePanEx(opl_voice_t *voice, unsigned int pan);
	void InitVoices(void);
	void VoiceKeyOff(opl_voice_t *voice);
	opl_channel_data_t *TrackChannelForEvent(unsigned char channel_num);
	void KeyOffEvent(unsigned char channel_num, unsigned char key);
	void ReplaceExistingVoice();
	void ReplaceExistingVoiceDoom1();
	void ReplaceExistingVoiceDoom2(opl_channel_data_t *channel);
	unsigned int FrequencyForVoice(opl_voice_t *voice);
	void UpdateVoiceFrequency(opl_voice_t *voice);
	void VoiceKeyOn(opl_channel_data_t *channel, const genmidi_instr_t *instrument, unsigned int instrument_voice,
	                unsigned int note, unsigned int key, unsigned int volume);
	void KeyOnEvent(unsigned char channel_num, unsigned char key, unsigned char volume);
	void ProgramChangeEvent(unsigned char channel_num, unsigned char instrument);
	void SetChannelVolume(opl_channel_data_t *channel, unsigned int volume);
	void SetChannelPan(opl_channel_data_t *channel, unsigned int pan);
	void AllNotesOff(opl_channel_data_t *channel, unsigned int param);
	void ControllerEvent(unsigned char channel_num, unsigned char controller, unsigned char param);
	void PitchBendEvent(unsigned char channel_num, unsigned char bend);
	void InitChannel(opl_channel_data_t *channel);

	public:
	virtual ~DoomOPL();

	const char *midi_synth_name(void);
	unsigned int midi_bank_count(void);
	const char *midi_bank_name(unsigned int bank);
	int midi_init(unsigned int rate, unsigned int bank, unsigned int extp);
	void midi_write(unsigned int data);
	void midi_generate(signed short *buffer, unsigned int length);
};
